package com.example.sort.service.impl;

import com.example.sort.constant.SortConstant;
import com.example.sort.entity.FreeNode;
import com.example.sort.entity.RectangleInfo;
import com.example.sort.service.ISortService;
import com.example.sort.sort.Demo1FitnessBuilder;
import com.example.sort.sort.Demo1SolutionConverter;
import mckaylib.ga.engine.GAEngine;
import mckaylib.ga.engine.core.EngineInvokeOptions;
import mckaylib.ga.engine.utils.MinMaxScaler;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

@Service
public class SortServiceImpl implements ISortService {
    public static List<RectangleInfo> initList;
    public static List<RectangleInfo> finalList;

    public void shipInit(List<RectangleInfo> rectangleList) {
        this.initList=rectangleList;
        finalList=new ArrayList<>();
    }

    /**
     * 算法排序
     */
    @Override
    public List<RectangleInfo> gaSort(List<RectangleInfo> rectangleList){
        //初始化基本数据
        shipInit(rectangleList);

        //需要指定一个线程池，因为GA算法需要很多运算
        ExecutorService executorService = Executors.newFixedThreadPool(1);

        //这个class中定义了GA算法所需的底层基因序列的定义、以及业务解决方案class与基因的互相转换逻辑
        Demo1SolutionConverter demo1SolutionConverter = new Demo1SolutionConverter();
        //这个class定义了适应度函数的指向（遗传算法非常依赖适应度函数，非常非常依赖，重中之重）
        Demo1FitnessBuilder fitnessBuilder = new Demo1FitnessBuilder();

        //引擎调用默认参数，如需改写默认参数单独修改即可
        EngineInvokeOptions options = new EngineInvokeOptions();
        options.setFitnessBuilder(fitnessBuilder);

        //设置为稳定适应度函数值默认
        options.setSteadyFitnessValue(SortConstant.SteadyFitness);
        //当连续多次的种群迭代的最优适应度函数值都稳定的时候，稳定就是分值没有超越当前的最好分值，然后才停止算法
        options.setEnableSteadyFitness(true);

        Integer result=0;

        MinMaxScaler minMaxScaler = new MinMaxScaler(100, false);

        GAEngine gaEngine = new GAEngine(50, executorService);

        Demo1SolutionConverter.Demo1Solution solution = (Demo1SolutionConverter.Demo1Solution) gaEngine.generate(demo1SolutionConverter, minMaxScaler, options,initList.size());

        List<Integer> orderList=solution.getSelectedIds();

        //List<Integer> orderList=Arrays.asList(new Integer[]{11, 3, 13, 4, 12, 9, 6, 0, 5, 1, 8, 2, 10, 7});
        //排序结果
        //System.out.println(orderList);

        //获取新的排序结果
        getNewSort(orderList);
        //调用排序逻辑
        sortList(this.finalList);
        return finalList;
    }


    public void sortList(List<RectangleInfo> list) {
        if(list==null || list.size()==0){
            throw new RuntimeException("没有矩形参数");
        }
        List<FreeNode> freeList=new ArrayList<>();
        //添加初始点位
        freeList.add(new FreeNode(0f,0f,SortConstant.length,SortConstant.width));

        for (RectangleInfo rectangleInfo : list) {
            //首先找出合适的点位,没有就下一个矩形，直到便利完成
            FreeNode freeNode=getLeftNode(freeList,rectangleInfo);
            if(freeNode==null){
                continue;
            }
            //首先增加两个点位信息和添加的矩形信息
            addNode(freeList,freeNode,rectangleInfo);
            //删除被覆盖的点位信息
            removeNode(freeList,rectangleInfo);
            //更新点位信息
            updateNode(freeList,rectangleInfo);
            //合并空余点位信息（目前没有想到好的解决方案，先合并两次）
            mergeNode(freeList);
            mergeNode(freeList);
            //空余点位重新排序，去重
            freeList=asc(freeList);
        }
    }

    //没添加完成后，对空余点位去重+排序
    public List<FreeNode> asc(List<FreeNode> list) {
        //去重，利用set不能添加重复的数据性能，重写hashcode和equals方法
        Set set = new HashSet();
        List newList = new ArrayList();
        set.addAll(list);//将list中的所有值添加到set中
        newList.addAll(set);
        try{
            //通过点位排序
            Collections.sort(newList, new Comparator<FreeNode>() {
                @Override
                public int compare(FreeNode o1, FreeNode o2) {
                    //return o1.getSort().compareTo(o2.getSort());
                    if (o1.getNodeX() > o2.getNodeX()) {
                        return 1;
                    } else if (o1.getNodeX() < o2.getNodeX()) {
                        return -1;
                    } else {
                        if (o1.getNodeY() < o2.getNodeY()) {
                            return -1;
                        } else {
                            return 1;
                        }
                    }
                }
            });
        }catch (Exception ex){
            System.out.println(newList.toString());
            System.out.println(ex.getMessage());
        }

        return newList;
    }

    /**
     * 通过增加的矩形信息增加两个点位(右上角和左下角的两个点位)
     * @param freeList
     * @param freeNode
     * @param rectangleInfo
     */
    public void addNode(List<FreeNode> freeList,FreeNode freeNode,RectangleInfo rectangleInfo){
        //设置矩形的坐标信息
        rectangleInfo.setNodeX(freeNode.getNodeX());
        rectangleInfo.setNodeY(freeNode.getNodeY());
        rectangleInfo.setNodeEndX(freeNode.getNodeX()+rectangleInfo.getLength());
        rectangleInfo.setNodeEndY(freeNode.getNodeY()+ rectangleInfo.getWidth());
        rectangleInfo.setIsOrder(true);
        //增加右上点位
        if(freeNode.getLength()-rectangleInfo.getLength()>SortConstant.minLength){
            FreeNode rightNode=new FreeNode(freeNode.getNodeX()+rectangleInfo.getLength(),freeNode.getNodeY(),freeNode.getLength()-rectangleInfo.getLength(),freeNode.getWidth());
            freeList.add(rightNode);
        }
        //增加左下点位
        if(freeNode.getWidth()-rectangleInfo.getWidth()>SortConstant.minWidth){
            FreeNode leftNode=new FreeNode(freeNode.getNodeX(),freeNode.getNodeY()+rectangleInfo.getWidth(),freeNode.getLength(),freeNode.getWidth()-rectangleInfo.getWidth());
            freeList.add(leftNode);
        }
    }

    /**
     * 移除点位,包含在填充矩形内部的点位移除
     * @param freeList
     * @param rectangleInfo
     */
    static void removeNode(List<FreeNode> freeList,RectangleInfo rectangleInfo){
        List<FreeNode> removeList=freeList.stream().filter(x->
                rectangleInfo.getNodeX()<=x.getNodeX()
                && rectangleInfo.getNodeY()<=x.getNodeY()
                && rectangleInfo.getNodeEndX()>x.getNodeX()
                && rectangleInfo.getNodeEndY()>x.getNodeY()
        ).collect(Collectors.toList());

        freeList.removeAll(removeList);
    }

    /**
     * 修改原有点位数据覆盖面积
     * @param freeList
     * @param rectangleInfo
     */
    void updateNode(List<FreeNode> freeList,RectangleInfo rectangleInfo){
        for (FreeNode freeNode : freeList) {
            calculateOverlapArea(freeNode,rectangleInfo);
        }
    }


    /**
     * 取出两个点位的交集面积，计算有交集的面积切割后的坐标信息
     * @param freeNode
     * @param rectangleInfo
     * @return
     */
    public double calculateOverlapArea(FreeNode freeNode, RectangleInfo rectangleInfo) {
        if (freeNode == null || rectangleInfo == null) {
            throw new RuntimeException("点位数据或者矩形数据为空，运行出错");
        }
        double p1_x = freeNode.getNodeX(), p1_y = freeNode.getNodeY();
        double p2_x = p1_x + freeNode.getLength(), p2_y = p1_y + freeNode.getWidth();
        double p3_x = rectangleInfo.getNodeX(), p3_y = rectangleInfo.getNodeY();
        double p4_x = p3_x + rectangleInfo.getLength(), p4_y = p3_y + rectangleInfo.getWidth();

        if (p1_x > p4_x || p2_x < p3_x || p1_y > p4_y || p2_y < p3_y) {
            return 0;
        }
        double Len = Math.min(p2_x, p4_x) - Math.max(p1_x, p3_x);
        double Wid = Math.min(p2_y, p4_y) - Math.max(p1_y, p3_y);
        if(Len*Wid>0){
            //交集面积的左上角点位
            Float intersectionX=(float)Math.max(p1_x, p3_x),intersectionY=(float)(Math.max(p1_y, p3_y));
            if(intersectionX.equals(freeNode.getNodeX())){
                freeNode.setWidth(intersectionY- freeNode.getNodeY());
                freeNode.setNodeEndY(intersectionY);
            }else if(intersectionY.equals(freeNode.getNodeY())){
                freeNode.setLength(intersectionX- freeNode.getNodeX());
                freeNode.setNodeEndY(intersectionX);
            }else{
                freeNode.setWidth(intersectionY- freeNode.getNodeY());
                freeNode.setNodeEndY(intersectionY);
                freeNode.setLength(intersectionX- freeNode.getNodeX());
                freeNode.setNodeEndY(intersectionX);
            }
            if(freeNode.getWidth()<=0 || freeNode.getLength()<=0){
                throw new RuntimeException("剩余空余面积计算出错，请检查代码");
            }
        }
        return Len * Wid;
    }


    /**
     * 取出大小合适，且在最左边的点
     * @param freeList
     */
    static FreeNode getLeftNode(List<FreeNode> freeList,RectangleInfo rectangleInfo){
        //freeList按x,y排序，直接循环，直到取出大小合适数据，有点位合适就返回数据，没有就返回空
        for (FreeNode freeNode : freeList) {
            if(freeNode.getLength()>=rectangleInfo.getLength() && freeNode.getWidth()>=rectangleInfo.getWidth()){
                return freeNode;
            }
        }
        return null;
    }

    /**
     * 合并点位
     * @param freeList
     */
    void mergeNode(List<FreeNode> freeList){
        List<FreeNode> mergeNodeList=new ArrayList<>();
        for (FreeNode freeNode : freeList) {
            for (FreeNode compareFreeNode : freeList) {
                FreeNode mergeNode=mergeNode(freeNode,compareFreeNode);
                if(mergeNode!=null){
                    mergeNodeList.add(mergeNode);
                    break;
                }
            }
        }
        //添加合并点位
        if(mergeNodeList.size()>0)
            freeList.addAll(mergeNodeList);
    }

    /**
     * 合并点位
     * @param freeNode
     * @param compareFreeNode
     * @return
     */
    public FreeNode mergeNode(FreeNode freeNode, FreeNode compareFreeNode) {
        if (freeNode == null || compareFreeNode == null) {
            throw new RuntimeException("点位数据或者矩形数据为空，运行出错");
        }
        double p1_x = freeNode.getNodeX(), p1_y = freeNode.getNodeY();
        double p2_x = p1_x + freeNode.getLength(), p2_y = p1_y + freeNode.getWidth();

        double p3_x = compareFreeNode.getNodeX(), p3_y = compareFreeNode.getNodeY();
        double p4_x = p3_x + compareFreeNode.getLength(), p4_y = p3_y + compareFreeNode.getWidth();


        if(p1_y==p4_y){
            //上下合并
            if(p3_x<=p1_x && p4_x>=p2_x){
                FreeNode mergeNode=new FreeNode(freeNode.getNodeX(),compareFreeNode.getNodeY(),freeNode.getLength(),freeNode.getWidth()+compareFreeNode.getWidth());
                return mergeNode;
            }
        }else if(p1_x==p4_x){
            //左右合并
            if(p3_y<=p1_y && p4_y>=p2_y){
                FreeNode mergeNode=new FreeNode(compareFreeNode.getNodeX(),freeNode.getNodeY(),freeNode.getLength()+compareFreeNode.getLength(),freeNode.getWidth());
                return mergeNode;
            }
        }
        return null;
    }

    /**
     * 获取船舶初始数据
     * @return
     */
    public List<RectangleInfo> getList(List<RectangleInfo> list){
        List<RectangleInfo> entityList=new ArrayList<>();
        for (RectangleInfo rectangleInfo : list) {
            RectangleInfo finalInfo=new RectangleInfo(rectangleInfo.getLength().floatValue(),rectangleInfo.getWidth().floatValue());
            entityList.add(finalInfo);
        }
        return entityList;
    }

    /**
     * 获取新的排序
     * @param listOrder
     */
    public void getNewSort(List<Integer> listOrder)
    {
        for (int i = 0; i < this.initList.size(); i++) {
            this.finalList.add(new RectangleInfo(this.initList.get(listOrder.get(i)).getLength(),this.initList.get(listOrder.get(i)).getWidth()));
        }
    }
}
